package com.yao2san.sim.storage.client.core.manager;

import com.aliyun.oss.OSS;
import com.aliyun.oss.model.ListObjectsRequest;
import com.aliyun.oss.model.OSSObjectSummary;
import com.aliyun.oss.model.ObjectListing;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.yao2san.sim.storage.client.util.StorageUtil;
import com.yao2san.sim.storage.client.config.AliOssProperties;
import com.yao2san.sim.storage.client.core.auth.Credentials;
import com.yao2san.sim.storage.client.core.enums.StorageType;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * @author wxg
 **/
@Slf4j
public class AliOssStorageManager extends AbstractStorageManager {


    private final AliOssProperties aliOssProperties;
    private final OSS ossClient;

    public AliOssStorageManager(OSS ossClient, AliOssProperties aliOssProperties) {
        this.aliOssProperties = aliOssProperties;
        this.ossClient = ossClient;
    }


    @Override
    public boolean exist(String object) {
        return ossClient.doesObjectExist(aliOssProperties.getBucket(), object);
    }

    @Override
    public void delete(String object) {
        ossClient.deleteObject(aliOssProperties.getBucket(), object);
    }

    @Override
    public void rename(String source, String target) throws IOException {
        ossClient.renameObject(aliOssProperties.getBucket(), source, target);
    }

    @Override
    public void copy(String source, String target, boolean keepSource) throws IOException {
        ossClient.copyObject(aliOssProperties.getBucket(), source, aliOssProperties.getBucket(), target);
    }

    @Override
    public List<StorageObject> list(String prefix) throws IOException {
        return list(prefix, null);
    }

    @Override
    public List<StorageObject> list(String prefix, String startAfter) throws IOException {
        String bucket = aliOssProperties.getBucket();
        String endpoint = aliOssProperties.getEndpoint();
        ListObjectsRequest request = new ListObjectsRequest();
        request.setBucketName(bucket);
        request.setPrefix(prefix);
        request.setMarker(startAfter);
        ObjectListing objects = ossClient.listObjects(request);
        List<OSSObjectSummary> objectSummaries = objects.getObjectSummaries();
        List<StorageObject> storageObjects = new ArrayList<>();
        objectSummaries.forEach(v -> {
            StorageObject storageObject = StorageObject.builder()
                    .object(v.getKey())
                    .size(v.getSize())
                    .lastModified(v.getLastModified().getTime())
                    .storageType(StorageType.ALI_OSS.name())
                    .url(getUrl(endpoint, bucket, v.getKey()))
                    .isDir(false)
                    .build();
            storageObjects.add(storageObject);

        });
        return storageObjects;
    }

    private String getUrl(String endpoint, String bucket, String object) {
        return bucket + "." + endpoint + "/" + object;
    }

    @Override
    public String getName() {
        return StorageType.ALI_OSS.name();
    }


    @Override
    public Credentials credentials(long durationSeconds) {
        Credentials cachedCredentials = getCachedCredentials();
        if (cachedCredentials != null) {
            return cachedCredentials;
        }
        String region = aliOssProperties.getRegion();
        String accessKey = aliOssProperties.getAccessKey();
        String secretKey = aliOssProperties.getSecretKey();
        String roleArn = aliOssProperties.getRoleArn();
        IClientProfile profile = DefaultProfile.getProfile(region, accessKey, secretKey);
        DefaultAcsClient client = new DefaultAcsClient(profile);
        final AssumeRoleRequest request = new AssumeRoleRequest();
        request.setSysMethod(MethodType.POST);
        request.setRoleArn(roleArn);
        request.setRoleSessionName(UUID.randomUUID().toString());
        request.setPolicy(null);
        request.setDurationSeconds(durationSeconds);
        try {
            final AssumeRoleResponse response = client.getAcsResponse(request);
            AssumeRoleResponse.Credentials credentials = response.getCredentials();
            Credentials c = Credentials.builder().accessKey(credentials.getAccessKeyId())
                    .secretKey(credentials.getAccessKeySecret())
                    .securityToken(credentials.getSecurityToken())
                    .expiration(credentials.getExpiration())
                    .build();
            cachedCredentials(c, durationSeconds);
            return c;
        } catch (ClientException e) {
            throw new RuntimeException(e);
        }
    }

    public Credentials getCachedCredentials() {
        return CREDENTIALS_CACHE.get(getKey());
    }

    public void cachedCredentials(Credentials credentials, long seconds) {
        CREDENTIALS_CACHE.put(getKey(), credentials, seconds);
    }

    public String getKey() {
        return StorageUtil.md5(this.aliOssProperties.toString());
    }
}
